using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace Boggle.Net.Model
{
    public sealed partial class Tray
    {
        [DebuggerDisplay("{Column}, {Row}")]
        public struct Location
        {
            private readonly int _column;
            private readonly int _row;

            public Location(int column, int row)
            {
                _column = column;
                _row = row;
            }

            public int Column
            {
                get { return _column; }
            }

            public int Row
            {
                get { return _row; }
            }

            public bool IsWithinBounds
            {
                get { return IsLocationWithinBounds(Column, Row); }
            }

            public IEnumerable<Location> NeighbouringLocations
            {
                get
                {
                    var minCol = Math.Max(Column - 1, 0);
                    var maxCol = Math.Min(Column + 1, Columns - 1);

                    var minRow = Math.Max(Row - 1, 0);
                    var maxRow = Math.Min(Row + 1, Rows - 1);

                    for (var row = minRow; row <= maxRow; ++row)
                    {
                        for (var column = minCol; column <= maxCol; ++column)
                        {
                            if (!(column == Column && row == Row))
                                yield return new Location(column, row);
                        }
                    }
                }
            }

            public override string ToString()
            {
                return String.Format("({0},{1})", Column, Row);
            }

            public bool Equals(Location location)
            {
                return location.Column == Column && location.Row == Row;
            }

            public override bool Equals(object obj)
            {
                return obj is Location && Equals((Location) obj);
            }

            public override int GetHashCode()
            {
                return Column ^ Row;
            }

            public static bool operator ==(Location left, Location right)
            {
                return left.Equals(right);
            }

            public static bool operator !=(Location left, Location right)
            {
                return !(left == right);
            }

            [Pure]
            public static bool IsLocationWithinBounds(int column, int row)
            {
                return column >= 0 && column < Columns && row >= 0 && row < Rows;
            }

            [ContractInvariantMethod]
            private void ObjectInvariants()
            {
                Contract.Invariant(IsWithinBounds);
            }
        }
    }
}